/***********************************************************************
 ***一个用于Linux串行通信的库
 ***作者：mario，2017.8.7
 ***版本：0.1
 ***特此授权任何获得本软件和相关文档文件（“软件”）副本的人免费处理本软件，
 ***包括但不限于使用，复制，修改，合并的权利，发布，分发，再授权和/或出售
 ***本软件的副本，并允许提供本软件的人员遵守以下条件：
 ***上述版权声明和本许可声明应包含在本软件的所有副本或主要部分。
 ***该软件“按原样”提供，不附带任何明示或暗示的保证，包括但不限于适销性，
 ***适用于特定用途和非侵权的担保。在任何情况下，作者或版权所有者均不对任何
 ***索赔，损害或其他责任负责，无论是否因与本软件或本软件的使用或其他交易相
 ***关的任何合同，侵权行为或其他方面的行为软件。
 ***********************************************************************/

#include "serial.h"

#ifndef CMSPAR
#define CMSPAR   010000000000 //注意这个是为了 MARK校验，SPACE校验
#endif

/************ 流控制 ********************/
#define FLOW_CONTROL_NONE         0
#define FLOW_CONTROL_HARDWARE     1
#define FLOW_CONTROL_SOFTWARE     2

/* 奇偶检验 */
#define PARITY_NONE  'N'
#define PARITY_EVEN  'E'
#define PARITY_ODD   'O'
#define PARITY_SPACE 'S'
#define PARITY_MARK  'M'

int speed_arr[]={B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300};
int name_arr[]={115200, 38400, 19200, 9600, 4800, 2400, 1200, 300};

/***********************************************************************
***函数功能：打开串口
***输入参数：*dev 设备名称
***返回值  ：fd 或者 FALSE
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int OpenDev(char *dev)
{
  int fd=open(dev,O_RDWR|O_NOCTTY|O_NDELAY);
  //int fd=open(dev,O_RDWR|O_NOCTTY);
  //int fd=open(dev,O_RDWR|O_NOCTTY|O_NONBLOCK);
  if(fd<0)
  {
	printf("Can't Open Serial Port!\n");
        return FALSE;
  }
  else
	return fd;
}

/***********************************************************************
***函数功能：设置波特率
***输入参数：fd:设备返回值，speed:波特率值，newtio:串口结构控制位设置值
***返回值  ：TRUE 或者 FALSE
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int Set_speed(int fd,int speed,struct termios *newtio)
{
  int i;
  int status;
  struct termios *Opt=newtio;
  tcgetattr(fd,Opt);
  for(i=0;i<sizeof(speed_arr)/sizeof(int);i++)
  {
	if(speed==name_arr[i])
	{
	  tcflush(fd,TCIOFLUSH);
	  cfsetispeed(Opt,speed_arr[i]);
	  cfsetospeed(Opt,speed_arr[i]);
	  printf("%d\n",i);
	  status=tcsetattr(fd,TCSANOW,Opt);
	  if(status !=0)
	  {
		printf("tcsetattr failed!\n");
		return FALSE;
	  }
	  tcflush(fd,TCIOFLUSH);
	}
  }
  return TRUE;
}

/***********************************************************************
***函数功能：设置fd的数据位、停止位、奇偶检验位
***输入参数：fd:设备返回值,databits:数据位,stopbits:停止位,parity:奇偶检验位,
            flow_ctrl:数据流控制,cmin:接收最小字节,times:超时时间（0.1s)
***返回值  ：TRUE 或者 FALSE
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
//int Set_attr(int fd,int databits,int stopbits,char parity,int flow_ctrl)
int Set_attr(int fd,int databits,int stopbits,char parity,int flow_ctrl,int cmin,int times)
{
   struct termios options;
   if(tcgetattr(fd,&options)!=0)
   {
	printf("Setup Serial 1 error!\n");
	return FALSE;
   }
   //对options的起始地址开始的termios结构体内存置零
   bzero(&options,sizeof(options));
   options.c_cflag &=~CSIZE;
   
   //设置数据流控制
   switch(flow_ctrl)
   {
	case FLOW_CONTROL_NONE :
		options.c_cflag &= ~CRTSCTS;
		break;
	case FLOW_CONTROL_HARDWARE :
		options.c_cflag |= CRTSCTS;
		break;
	case FLOW_CONTROL_SOFTWARE :
		options.c_cflag |= IXON | IXOFF | IXANY;
		break;
	default:
		printf("Unsupported flow_ctrl!\n");
		return FALSE;
		
   }
   //选择数据位
   switch(databits)
   {
	case 5:
		options.c_cflag |=CS5;
		break;	
	case 6:
		options.c_cflag |=CS6;
		break;	
	case 7:
		options.c_cflag |=CS7;
		break;
	case 8:
		options.c_cflag |=CS8;
		break;
	default:
		printf("Unsupported data size!\n");
		return FALSE;
   }
   //选择奇偶校验
   switch(parity)
   {
	case PARITY_NONE://无校验 N
		options.c_cflag &=~(PARENB);
		options.c_cflag &=~(PARODD);
		//options.c_cflag &=~(INPCK);
		options.c_cflag |=INPCK;
		//printf("df\n");
		break;
	//case 'o'://奇校验
	case PARITY_ODD:
		options.c_cflag |=(PARODD|PARENB);
		options.c_cflag |=INPCK;
		break;
	//case 'e'://偶校验
	case PARITY_EVEN:
		options.c_cflag |=PARENB;
		options.c_cflag &=~(PARODD);
		options.c_cflag |=INPCK;
		break;
	//case 's':
	case PARITY_SPACE:
		options.c_cflag |= PARENB | CMSPAR;
		options.c_cflag &= ~PARODD;
		break;
        //case 'm':
	case PARITY_MARK:
		options.c_cflag |= PARENB | CMSPAR | PARODD;
		break;
	default:
		printf("Unsupported parity!\n");
		return FALSE;
   }
   switch(stopbits)
   {
	case 1:
		options.c_cflag &=~(CSTOPB);
		break;
	case 2:
		options.c_cflag |=CSTOPB;
		break;
	default:
		printf("Unsupported stop bits!\n");
		return FALSE;
   }

   if(parity !='N')
   {
	//修改控制模式，保证程序不会占用串口
	options.c_cflag |=CLOCAL;
	//修改控制模式，使得能够从串口中读取输入数据
	options.c_cflag |=CREAD;
	options.c_cflag &=~(ICANON|ECHO|ECHOE);
	options.c_cflag &=~(IXON|IXOFF|IXANY);
	options.c_cflag &=~(ICANON|ECHO|ECHOE|ISIG);
	options.c_cflag &=~OPOST;
   }

   //有字符处理或经过TIME个0.1秒后返回
   options.c_cc[VTIME]=times;
   options.c_cc[VMIN]=cmin;//0 最小接收字符
   //如果发生数据溢出，接收数据，但是不再读取
   tcflush(fd,TCIFLUSH);
   //激活配置（将修改后的termios数据设置到串口中）
   if(tcsetattr(fd,TCSANOW,&options)!=0)
   {
	printf("Setup Serial error!\n");
	return FALSE;
   }
   return TRUE;
}

/***********************************************************************
***函数功能：写数据函数
***输入参数：fd:设备文件，buf:发送缓存区，data_len:数据长度
***返回值  ：write_count:发送字节数
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int Write_Data(int fd, void *buf, size_t data_len)
//int Write_Data(int fd,char *buf,size_t data_len)
{
   int m_fd=fd;
   int write_count=0;
   ssize_t nwrite=0;
   
   if(m_fd<0)
   {
	printf("Please Open The Device File!\n");
   }
   while(data_len>0)
   {
	nwrite = write(fd, (char*)buf + write_count, data_len);	
	//nwrite=write(fd,buf,data_len);
	if(nwrite<1)
	{
	  printf("Write Data Fail!\n");
	  break;
	}
	write_count +=nwrite;
	data_len -=nwrite;
   }
   tcflush(fd,TCIOFLUSH);// 清除所有正在发生的I/O数据。
   //tcflush(fd, TCOFLUSH);// 清除正写入的数据，且不会发送至终端。
   return write_count;
}

/***********************************************************************
***函数功能：读取数据函数
***输入参数：fd:设备文件
***返回值  ：buff:接收缓存区
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int Read_Data(int fd,un_char *buff,size_t data_len)
{
   int nread=0;
   int fd_max;
   int nselect;

   fd_set readfds;
   FD_ZERO(&readfds);//清空集合
   FD_SET(fd,&readfds);//加入集合 集合中删除FD_CLR
   fd_max=fd+1;

   nselect=select(fd_max,&readfds,NULL,NULL,NULL);
   memset(buff,0,sizeof(*buff));//sizeof（*buff）加*解决警告问题

   if(nselect<=0)
	printf("select failed");
   else if(FD_ISSET(fd,&readfds)>0)//是否可以读写FD_ISSET
   {
	//nread=read(fd,buff,8);//只能接收8个字符
	nread=read(fd,buff,data_len);//只能接收20个字符 需要配合 options.c_cc[VMIN]=20;
	//buff[nread]='\n';//打印出来值  0xa
   }
   int j=0;
   while(buff[j]!='\0')
   {
	printf("the readable data is 0x%x\n",buff[j]);
	j++;
   }
   return nread;
}
